##
# @file 最终整合测试文件
# @author BlueBaysalt
# @date 2024/11/17
# @brief 将读取，打印，写入都整合在一起的一个文件
# @attention ‘@ENJ’打印调试后面的标记，帮助定位到打印语句
# #

import re  # 正则表达式
import tkinter as tk

# 常量定义
NOT_EXIST = -1  # 用于find()方法，返回-1表示没找到
# flag标志的值
# F_NOT = 0  # “否”标志
# F_YES = 1  # "是"标志
F_N_FIND = 0  # “没找到”标志
F_FIND = 1  # “找到”标志

MY_HEAD_TIP_STRING = ("=#==>这是一个基于机器人竞赛许俊辉学长地图代码的可视化处理程序。<==#=\n"
                      "<注意>以下情况会使程序坏掉(Crash, Freeze, Fail, Abort, Breakdown)\n"
                      "1、刘攀自己写的程序有BUG；\n"
                      "2、文件中数据格式不符合许俊辉学长的规范；\n"
                      "3、文件中数据不完整；\n"
                      "请输入你想要处理的地图文件名（文件路径地址）：")

# 定义变量
raw_map_dict = {}  # 未处理的地图数据字典对象

grid_map_row = 20  # 网格化地图的行数
grid_map_col = 20  # 网格化地图的列数

read_data_line_num = 20  # 读取多少行的数据


def parse_complex_string(s):
    ##
    # @name 解析复杂字符串
    # @para s 字符串
    # @func 去除字符串中的空格和换行符，按逗号进行分隔
    # @return 一个包含多个字符串的列表
    # #

    # 使用正则表达式去除多余的空格和换行符，但保留逗号作为分隔符
    cleaned_string = re.sub(r'[\s\n]+(?=,|$)', '', s)
    # 按逗号分割字符串
    parts = cleaned_string.split(',')
    # 清理每个部分（去除前后的空格和引号等）
    cleaned_parts = [part.strip().strip('"') for part in parts]
    return cleaned_parts


def replace_char(original_string, chars_to_replace, replacement):
    ##
    # @name 替换字符
    # @para original_string 目标字符串
    # @para chars_to_replace 目标字符串中想要被替换的子串
    # @para replacement 用什么字符来替换
    # @func 用指定字符替换字符串中的子串
    # @return 字符串
    # #
    trans_table = original_string.replace(chars_to_replace, replacement)
    return trans_table


def print_main_map_dict(map_dict):
    # 打印结果
    print("@ENJ: 地图绘制相关数据如下：")
    for key, values in map_dict.items():
        print(key, ':', values)


def print_map_dict(map_dict, data_row, data_col):
    ##
    # @name 打印地图字典内容(暂时对主地图的数据没法打印)
    # @para map_dict 要打印的字典对象，字典内形式为{(行，列):内容}
    # @para data_row key中的行数
    # @para data_cow key中的列数
    # #
    # 打印结果
    data_key_list = list(map_dict.keys())
    if "NONE" in data_key_list:
        return None
    for row in range(data_row):
        for col in range(data_col):
            if (row, col) in data_key_list:
                # data_key_list_iner_key = list(map_dict[(row, col)].keys())
                # if '坐标' in list(map_dict[(row, col)].values()):
                #     data_value = map_dict[(row, col)]['坐标']
                #     print('(', row, ', ', col, '):', data_value, " ", end="")  # @ENJ
                # else:
                data_value = map_dict[(row, col)]
                print(data_key_list[row * data_row + col], ':', data_value, " ", end="")  # @ENJ
            else:
                continue

        print("")


def processing_map_dict(raw_dict):
    ##
    # @name 处理地图字典
    # @para 单一维度值的字典，特指map.h中地图数据处理后得到的raw_map_dict
    #       raw_map_dict={(0, 0): ['shan1', 'zhudian', '{', '0.00', '0.00', '0.00', '0.00',
    #           '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '}', '{', '0', '0', '0', '0', '0',
    #               '0', '0', '0', '0', '0', '}', '{', '-1.93', '0.00', '}', 'nodes____', '0', '{', '0', '0', '}']}
    # @attention 如何加工的请看代码实现，这个具有独特性，其他字典无法处理
    # @return 返回一个嵌套字典，其内的值全为字符串类型
    # #

    map_dict = {}  # 初始化嵌套字典
    l_brace_index_list = []
    r_brace_index_list = []
    for key, values in raw_dict.items():
        for index in range(len(values)):
            if values[index] == "{":
                l_brace_index_list.append(index)
            elif values[index] == "}":
                r_brace_index_list.append(index)
        point_name = values[0]
        point_type = values[1]
        # 由于“距离”在你的数据中不明确，我们可以选择忽略它或将整个剩余部分作为数据
        # 这里我们假设不直接提取“距离”，而是将剩余部分作为“数据”字段的值（作为字符串列表）
        point_distance = values[l_brace_index_list[0] + 1:r_brace_index_list[0]]  # 第一个大括号包含的是距离信息
        point_speed = values[l_brace_index_list[1] + 1:r_brace_index_list[1]]  # 第二个大括号包含的是速度信息
        point_coordinate = values[l_brace_index_list[2] + 1:r_brace_index_list[2]]  # 第三个大括号包含的是实际坐标
        point_info_1 = values[r_brace_index_list[2] + 1]
        point_info_2 = values[r_brace_index_list[2] + 2]
        point_jump = values[l_brace_index_list[3] + 1:r_brace_index_list[3]]  # 第四个大括号包含的是跳跃信息
        # 创建一个嵌套字典并添加到map_dict中
        map_dict[key] = {
            '名称': point_name,
            '类型': point_type,
            '距离': point_distance,  # 如果需要，可以进一步处理这个字段
            '速度': point_speed,
            '坐标': point_coordinate,
            '信息1': point_info_1,
            '信息2': point_info_2,
            '跳跃': point_jump
        }
    return map_dict


def read_and_process_map(file_name):
    ##
    # @name 读取然后处理地图数据
    # @para file_name 文件完整名
    # @func 读取文件，找到对应数据，然后分隔数据，存入嵌套字典中
    # @return 存有有效数据字符的嵌套字典
    # #
    # 打开文件‘elaborate_map.h'
    with open(file_name, 'r', encoding='utf-8') as file:
        # global end_index
        # 遍历文件的每一行
        for txt_line in file:
            str_is_exist = txt_line.find("map_Square_t")
            # 查看这一行中是否有“map_Square_t”内容
            if str_is_exist != NOT_EXIST:
                # print(is_exist)   # @ENJ:打印每行内容里map_Square_t出现的位置
                # 查找一行里第一个“{”的位置
                start_index = txt_line.find('{')
                if start_index == NOT_EXIST:
                    continue  # 忽略掉不含‘{’的行
                # 由于存在嵌套大括号，我们需要找到与第一个左大括号相匹配的右大括号
                # 这里我们采用一个计数器来追踪大括号的开闭情况
                brace_count = 1  # 大括号计数
                end_index = 0  # 最后一个“}”的位置
                for position, char in enumerate(txt_line[start_index + 1:], start=start_index + 1):
                    if char == '{':
                        brace_count += 1
                    elif char == '}':
                        brace_count -= 1
                    if brace_count == 0:
                        end_index = position  # end_index记录最后一个“}”的位置
                        break
                # 提取并打印最外层大括号里的内容
                brace_content = txt_line[start_index:end_index + 1]
                # 确定大括号在这一行里闭合，可以先排除注释行，再开始记下有效行里数据对应的行列号
                str_static_index = txt_line.find("static")  # 查找每一行中“static”的位置
                if "//" in txt_line[:str_static_index]:  # 在“static”前查看是否包含“//”
                    continue  # 包含注释符则跳过此行内容
                # 定义正则表达式模式
                pattern = r"map_\d{2}_\d{2}"  # 内容为：map_两位数字_两位数字
                # 使用 re.findall() 查找所有匹配的子字符串
                row_col_inf = re.findall(pattern, txt_line)  # 内容为：{ ... }
                # print(row_col_inf)  # @ENJ:打印行列信息
                # print(brace_content)  # @ENJ:打印大括号内的内容
                # 删除brace_content中无用的空格，引号
                data_str_1 = brace_content[1:-1]  # 去掉最前后最后的大括号
                # print(data_str_1) # @ENJ:去掉前后大括号
                data_str_2 = replace_char(data_str_1, " ", "")
                # print(data_str_2) # @ENJ:去掉空格
                data_str_3 = replace_char(data_str_2, "\"", "")
                # print(data_str_3)   # @ENJ:去掉引号
                # 接下来在所有左大括号后加上一个逗号‘,’
                data_str_4 = ''
                last_l_brace_index = 0
                for count, ch in enumerate(data_str_3):
                    if ch == "{":
                        data_str_4 = data_str_4 + data_str_3[last_l_brace_index:count + 1] + ','
                        last_l_brace_index = count + 1
                data_str_4 = data_str_4 + data_str_3[last_l_brace_index:]
                # print(data_str_4) #@ENJ
                # 接下来在第三个，第四个右括号“}”前加上逗号“，”
                last_r_brace_index = 0
                r_brace_count = 0
                data_str_5 = ""
                for count, ch in enumerate(data_str_4):
                    if ch == "}":
                        r_brace_count += 1
                        if r_brace_count >= 3:
                            data_str_5 = data_str_5 + data_str_4[last_r_brace_index:count] + ','
                            last_r_brace_index = count
                data_str_5 = data_str_5 + '}'
                # print(data_str_5)   # @ENJ
                # 将行列信息和大括号里的内容放入字典对象中
                # 使用正则表达式提取数字
                extracted_numbers = []
                for item in row_col_inf:
                    # 查找所有数字序列（\d+ 匹配一个或多个数字）
                    matches = re.findall(r'\d+', item)
                    # 将匹配到的数字序列（字符串形式）添加到列表中
                    extracted_numbers.append(matches)
                # 由于我们知道每个字符串都有两个数字，我们可以将它们转换为整数并分别存储
                row_cols = []
                for nums in extracted_numbers:
                    # 转换字符串为整数，并作为元组添加到新列表中
                    row_cols.append((int(nums[0]), int(nums[1])))
                # print(row_cols)  # @ENJ:以数字形式打印行列号，如(0, 9)
                # 遍历row_cols和brace_content（假设它们长度相同）
                for row_col in row_cols:
                    data = parse_complex_string(data_str_5)
                    # 将元组作为键，列表作为值放入字典中
                    raw_map_dict[row_col] = data
                # print(raw_map_dict)  # @ENJ:打印未作加工的，以行列号元组为键，地图数据字符串为值的地图数据字典
    # 将未加工的地图数据字典进行加工
    processed_map_dict = processing_map_dict(raw_map_dict)
    # 打印结果
    # for key, values in processed_map_dict.items():
    #     print(key, ':', values)

    return processed_map_dict


def get_specify_map_data(file_name, data_key_str):
    ##
    # @name 读取然后处理特定地图数据（除了主数据其他都处理，例如A星地图数据）
    # @para file_name 文件完整名
    # @para data_key_str 特定地图数据的名字，可选字符串如下：
    #                       "a星算法地图"
    #                       "自定义转弯速度"
    #                       "自定义节点到车中心距离"
    #                       "自定义节点pt到车中心距离"
    #                       "自定义转弯角度偏移"
    #                       "自定义检测节点速度"
    # @func 读取文件，找到对应数据，然后分隔数据，存入嵌套字典中
    # @return 存有有效数据字符的嵌套字典
    # #

    # 定义变量
    map_dat_dict = {}  # 未处理的地图数据字典对象

    # 文件中数据行数计数的初值
    read_data_line_count = -1  # 读地图数据行计数

    # 查找数据用的字符串保存在字典中，里面的字符串要求与文件中的一样
    data_name_str = {
        "a星算法地图": "Astart_map[ROWS][COLS]",
        "自定义转弯速度": "turn_speed[ROWS][COLS]",
        "自定义节点到车中心距离": "sensor_2_car_center[ROWS][COLS]",
        "自定义节点pt到车中心距离": "sensor_2_car_center_pt[ROWS][COLS]",
        "自定义转弯角度偏移": "turn_angle_offset[ROWS][COLS]",
        "自定义检测节点速度": "check_node_speed[ROWS][COLS]"
    }

    # 标志变量定义
    find_data_flag = F_N_FIND  # 找没找到a星算法地图数据的标志，默认为0，找到为1
    # 记得换掉open()里的文件地址，用一个全局变量代替
    with open(file_name, 'r', encoding='utf-8') as file:
        # global end_index
        # 遍历文件的每一行
        for txt_line in file:
            ########## 寻找a星算法地图数据 ########## 2024/11/28
            str_is_exist = txt_line.find(data_name_str[data_key_str])
            # 查看这一行中是否有“Astart_map[ROWS][COLS]”内容
            if str_is_exist != NOT_EXIST:
                # print(txt_line)
                # print(str_is_exist)  # @ENJ:打印每行内容里map_Square_t出现的位置
                # 找到"Astart_map[ROWS][COLS]"的位置，下方20行为数据，挂起标志
                find_data_flag = F_FIND
                read_data_line_count = 0  # a星算法地图数据行数
                print("@ENJ: 读取到 <" + data_key_str + "> " + data_name_str[
                    data_key_str] + " 数据。")  # @ENJ:(一般不用关闭)打印此次寻找的数据属于哪一块儿内容
                continue
            if find_data_flag and (0 <= read_data_line_count < read_data_line_num):
                # 查看每行内容前10个字符串里是否有注释符号
                if "//" in txt_line[:10]:
                    continue  # 如果发现此行是数据前一行注释内容，跳过这一行
                # 处理数据行的字符串
                # print(str(read_data_line_count) + ": " + txt_line)  # @ENJ：打印行号和行内数据
                # 提取行内有效字符
                # 将字符串中的空格，换行符去掉
                txt_line_1 = txt_line.strip()
                # 去掉字符串中的空格
                txt_line_2 = replace_char(txt_line_1, " ", "")
                # 找到左右大括号的位置，然后提取大括号中间的内容
                l_brace_index = txt_line_2.find("{")
                r_brace_index = txt_line_2.find("}")
                # 不要左右大括号和有大括号前的逗号
                txt_line_3 = txt_line_2[l_brace_index + 1: r_brace_index - 1]
                # print(txt_line_3)  # 打印去空格和换行符的字符串
                data_list = txt_line_3.split(',')
                # print(data_list)  # @ENJ:打印列表
                for col_count in range(len(data_list)):
                    map_dat_dict[(read_data_line_count, col_count)] = data_list[
                        col_count]  # 使不使用eval()好像没差别，要用就把下面那个解注释
                    # map_dat_dict[(read_data_line_count, col_count)] = eval(data_list[col_count])    # 将每一个数据从字符串类型变为数字类型
                read_data_line_count += 1
                # 不是注释内容,且数据行计数没有到read_data_line_num(有20行要读取)则
                if read_data_line_count == read_data_line_num:
                    return map_dat_dict
            #         find_data_flag = F_N_FIND
            # if find_data_flag == F_N_FIND and read_data_line_count == read_data_line_num:
            #     return map_dat_dict
    print("[ERROR] 读取 <" + data_key_str + "> " + data_name_str[
        data_key_str] + " 数据出现问题。")  # @ENJ:(一般不用关闭)打印没找到数据的提示消息
    return {"NONE": "NONE"}


def draw_grid_map(canvas, rows, cols, square_size, dict):
    ##
    # @name 绘制网格地图
    # @para canvas 画布对象
    # @para rows 绘制多少行
    # @para cols 绘制多少列
    # @para square_size 正方形的大小
    # @para dict 数据来源，字典类型
    # @func 根据地图数据绘制网格地图
    # #
    # colors = ["red", "blue", "green", "yellow", "purple", "orange", "cyan", "magenta"]
    # global color
    # 所有点的类型及对应颜色
    type2color = {
        'None': "green",  # 未找到的就是None
        'zhudian': "magenta",
        'pingtai': "yellow",
        'xinxi__': "blue",
        'jiedian': "orange",
    }
    for row in range(rows):
        for col in range(cols):
            x0 = (col + 1) * square_size
            y0 = (row + 1) * square_size
            x1 = x0 + square_size
            y1 = y0 + square_size
            # 行列号是否在地图字典里
            if (row, col) in dict.keys():
                color = type2color[dict[(row, col)]['类型']]
                canvas.create_rectangle(x0, y0, x1, y1, fill=color)  # 绘制并填充方格
            else:
                color = type2color['None']
                canvas.create_rectangle(x0, y0, x1, y1, fill=color)  # 绘制并填充方格


def main():
    # file_name = 'elaborate_map.h'
    file_name = input(MY_HEAD_TIP_STRING)
    main_map_dict = read_and_process_map(file_name)  # 读取并处理地图主数据
    print_main_map_dict(main_map_dict)
    # print_map_dict(main_map_dict, grid_map_row, grid_map_col)  # 按照行列格式化打印地图字典内容，对主地图数据暂时不可用
    # 以下是地图次要数据的读取和处理
    sub_1_map_dict = get_specify_map_data(file_name, "a星算法地图")
    print_map_dict(sub_1_map_dict, grid_map_row, grid_map_col)  # 按照行列格式化打印地图字典内容
    sub_2_map_dict = get_specify_map_data(file_name, "自定义转弯速度")
    print_map_dict(sub_2_map_dict, grid_map_row, grid_map_col)  # 按照行列格式化打印地图字典内容
    sub_3_map_dict = get_specify_map_data(file_name, "自定义节点到车中心距离")
    print_map_dict(sub_3_map_dict, grid_map_row, grid_map_col)  # 按照行列格式化打印地图字典内容
    sub_4_map_dict = get_specify_map_data(file_name, "自定义节点pt到车中心距离")
    print_map_dict(sub_4_map_dict, grid_map_row, grid_map_col)  # 按照行列格式化打印地图字典内容
    sub_5_map_dict = get_specify_map_data(file_name, "自定义转弯角度偏移")
    print_map_dict(sub_5_map_dict, grid_map_row, grid_map_col)  # 按照行列格式化打印地图字典内容
    sub_6_map_dict = get_specify_map_data(file_name, "自定义检测节点速度")
    print_map_dict(sub_6_map_dict, grid_map_row, grid_map_col)  # 按照行列格式化打印地图字典内容

    root = tk.Tk()
    root.title("网格化虚拟地图")
    root.attributes('-topmost', True)
    # 设置窗口大小，假设我们要绘制一个20x20的方格网，每个方格大小为30像素
    # 因此，窗口的总大小应该是 600x600 像素（不包括边框和标题栏）
    square_size = 25  # 网格化地图每个各自的长，宽像素个数
    window_width = square_size * (grid_map_row + 2)  # 窗口的像素宽度
    window_height = square_size * (grid_map_col + 2)  # 窗口的像素高度
    rows = cols = window_width // square_size
    # 创建一个画布
    canvas = tk.Canvas(root, width=window_width, height=window_height)
    canvas.pack()
    # 绘制地图
    draw_grid_map(canvas, rows, cols, square_size, main_map_dict)

    root.mainloop()


if __name__ == "__main__":
    main()
